/*
 * @Descripttion: babel通用替换
 * @Version: 1.0
 * @Author: undead
 * @Date: 2022-01-13 10:56:31
 * @LastEditors: undead
 * @LastEditTime: 2022-01-14 09:34:11
 */
const types = require("@babel/types");
class utils {
  /** function转成String */
  fnConverString(path) {
    var node = path.node;
    // 判断节点类型及函数名，不是则返回
    if (
      !types.isIdentifier(node.callee, {
        name: "",
      })
    )
      return;
    // 调用解密函数
    let value = eval(path.toString());
    console.log("还原前：" + path.toString(), "还原后：" + value);
    path.replaceWith(types.valueToNode(value));
  }
  /* 十六进制文本还原 */
  restoreExtra(path) {
    delete path.node.extra;
  }
  /** 反控制流平坦化 */
  replaceWhile(path) {
    var node = path.node;
    // 判断是否是目标节点
    if (
      !(types.isBooleanLiteral(node.test) || types.isUnaryExpression(node.test))
    )
      // 如果while中不为true或!![]
      return;
    if (!(node.test.prefix || node.test.value))
      // 如果while中的值不为true
      return;
    if (!types.isBlockStatement(node.body)) return;
    var body = node.body.body;
    if (
      !types.isSwitchStatement(body[0]) ||
      !types.isMemberExpression(body[0].discriminant) ||
      !types.isBreakStatement(body[1])
    )
      return;

    // 获取数组名及自增变量名
    var swithStm = body[0];
    var arrName = swithStm.discriminant.object.name;
    var argName = swithStm.discriminant.property.argument.name;
    let arr = [];

    // 找到path节点的前一个兄弟节点，即数组所在的节点，然后获取数组
    let all_presibling = path.getAllPrevSiblings();
    // console.log(all_presibling)
    all_presibling.forEach((pre_path) => {
      const { declarations } = pre_path.node;
      let { id, init } = declarations[0];
      if (arrName == id.name && init.callee.object.value) {
        // 数组节点
        arr = init.callee.object.value.split("|");
        pre_path.remove();
      }
      if (argName == id.name) {
        // 自增变量节点
        pre_path.remove();
      }
    });

    // SwitchCase节点集合
    var caseList = swithStm.cases;
    // 存放按正确顺序取出的case节点
    var resultBody = [];
    arr.map((targetIdx) => {
      var targetBody = caseList[targetIdx].consequent;
      // 删除ContinueStatement块(continue语句)
      if (types.isContinueStatement(targetBody[targetBody.length - 1]))
        targetBody.pop();
      resultBody = resultBody.concat(targetBody);
    });
    path.replaceInline(resultBody);
  }
  /** 将拆分的对象重新合并 */
  mergeObject(path) {
    const { id, init } = path.node;
    if (!types.isObjectExpression(init)) return;

    let name = id.name;
    let properties = init.properties;

    let scope = path.scope;
    let binding = scope.getBinding(name);
    if (!binding || binding.constantViolations.length > 0) {
      return;
    }
    let paths = binding.referencePaths;
    paths.map(function (refer_path) {
      let bindpath = refer_path.parentPath;
      if (!types.isVariableDeclarator(bindpath.node)) return;
      let bindname = bindpath.node.id.name;
      bindpath.scope.rename(bindname, name, bindpath.scope.block);
      bindpath.remove();
    });
    scope.traverse(scope.block, {
      AssignmentExpression: function (_path) {
        const left = _path.get("left");
        const right = _path.get("right");
        if (!left.isMemberExpression()) return;
        const object = left.get("object");
        const property = left.get("property");
        if (
          object.isIdentifier({
            name: name,
          }) &&
          property.isStringLiteral() &&
          _path.scope == scope
        ) {
          properties.push(
            types.ObjectProperty(
              types.valueToNode(property.node.value),
              right.node
            )
          );
          _path.remove();
        }
        if (
          object.isIdentifier({
            name: name,
          }) &&
          property.isIdentifier() &&
          _path.scope == scope
        ) {
          properties.push(
            types.ObjectProperty(
              types.valueToNode(property.node.name),
              right.node
            )
          );
          _path.remove();
        }
      },
    });
  }
  /** 将对象进行替换 */
  callToString(path) {
    var node = path.node;

    if (!types.isObjectExpression(node.init)) return;

    // 获取对象内所有属性
    var objPropertiesList = node.init.properties;

    if (objPropertiesList.length == 0) return;

    // 对象名
    var objName = node.id.name;
    // 是否可删除该对象：发生替换时可删除，否则不删除
    var del_flag = false;

    objPropertiesList.forEach((prop) => {
      var key = prop.key.value;
      if (types.isFunctionExpression(prop.value)) {
        var retStmt = prop.value.body.body[0];

        // 该path的最近父节点
        var fnPath = path.getFunctionParent();
        fnPath.traverse({
          CallExpression: function (_path) {
            if (!types.isMemberExpression(_path.node.callee)) return;

            var _node = _path.node.callee;
            if (
              !types.isIdentifier(_node.object) ||
              _node.object.name !== objName
            )
              return;
            if (
              !types.isStringLiteral(_node.property) ||
              !types.isIdentifier(_node.property)
            )
              return;
            if (!_node.property.value == key || !_node.property.name == key)
              return;
            // if (!t.isStringLiteral(_node.property) || _node.property.value != key)
            //     return;

            var args = _path.node.arguments;

            // 二元运算
            if (
              types.isBinaryExpression(retStmt.argument) &&
              args.length === 2
            ) {
              _path.replaceWith(
                types.binaryExpression(
                  retStmt.argument.operator,
                  args[0],
                  args[1]
                )
              );
            }
            // 逻辑运算
            else if (
              types.isLogicalExpression(retStmt.argument) &&
              args.length == 2
            ) {
              _path.replaceWith(
                types.logicalExpression(
                  retStmt.argument.operator,
                  args[0],
                  args[1]
                )
              );
            }
            // 函数调用
            else if (
              types.isCallExpression(retStmt.argument) &&
              types.isIdentifier(retStmt.argument.callee)
            ) {
              _path.replaceWith(types.callExpression(args[0], args.slice(1)));
            }
            del_flag = true;
          },
        });
      } else if (types.isStringLiteral(prop.value)) {
        var retStmt = prop.value.value;

        // 该path的最近父节点
        var fnPath = path.getFunctionParent();
        fnPath.traverse({
          MemberExpression: function (_path) {
            var _node = _path.node;
            if (
              !types.isIdentifier(_node.object) ||
              _node.object.name !== objName
            )
              return;
            if (
              !types.isStringLiteral(_node.property) ||
              !types.isIdentifier(_node.property)
            )
              return;
            if (!_node.property.value == key || !_node.property.name == key)
              return;
            // if (!t.isStringLiteral(_node.property) || _node.property.value != key)
            //     return;

            _path.replaceWith(types.stringLiteral(retStmt));
            del_flag = true;
          },
        });
      }
    });
    if (del_flag) {
      // 如果发生替换，则删除该对象
      path.remove();
    }
  }
  /** 自执行函数实参替换形参 */
  convParam(path) {
    var node = path.node;

    if (!types.isCallExpression(node.expression)) return;

    if (
      node.expression.arguments == undefined ||
      node.expression.callee.params == undefined ||
      node.expression.arguments.length > node.expression.callee.params.length
    )
      return;

    var argumentList = node.expression.arguments;
    var paramList = node.expression.callee.params;
    for (var i = 0; i < argumentList.length; i++) {
      var argumentName = argumentList[i].name;
      var paramName = paramList[i].name;

      path.traverse({
        MemberExpression: function (_path) {
          var _node = _path.node;
          if (
            !types.isIdentifier(_node.object) ||
            _node.object.name !== paramName
          )
            return;

          _node.object.name = argumentName;
        },
      });
    }
    node.expression.arguments = [];
    node.expression.callee.params = [];
  }
  /** 去除逗号表达式 */
  removeComma(path) {
    let { expression } = path.node;
    if (!types.isSequenceExpression(expression)) return;
    let body = [];
    expression.expressions.forEach((express) => {
      body.push(types.expressionStatement(express));
    });
    path.replaceInline(body);
  }
  /** 去除var定义的逗号表达式 */
  removeVarComma(path) {
    let { kind, declarations } = path.node;
    if (declarations.length < 2) return;

    // 如果在for循环中，则不处理
    if (types.isForStatement(path.parentPath)) return;

    let temp = [];
    declarations.forEach((VariableDeclarator) => {
      // 将VariableDeclarator构造成variableDeclaration传入temp
      temp.push(types.variableDeclaration(kind, [VariableDeclarator]));
    });
    // 多个path替换一个path
    path.replaceInline(temp);
  }
  /** 三元表达式转if-else */
  ConditionToIf(path) {
    let { id, init } = path.node;
    if (!types.isConditionalExpression(init)) return;

    const ParentPath = path.parentPath;
    const ParentNode = path.parent;
    if (!types.isVariableDeclaration(ParentNode)) return;

    if (types.isForStatement(ParentPath.parentPath)) return;

    let kind = ParentNode.kind;
    let { test, consequent, alternate } = init;
    ParentPath.replaceWith(
      types.ifStatement(
        test,
        types.blockStatement([
          types.variableDeclaration(kind, [
            types.variableDeclarator(id, consequent),
          ]),
        ]),
        types.blockStatement([
          types.variableDeclaration(kind, [
            types.variableDeclarator(id, alternate),
          ]),
        ])
      )
    );
  }
  /** var定义的三元表达式转if-else */
  conditionVarToIf(path) {
    let { expression } = path.node;
    if (!types.isConditionalExpression(expression)) return;
    let { test, consequent, alternate } = expression;
    path.replaceWith(
      types.ifStatement(
        test,
        types.blockStatement([types.expressionStatement(consequent)]),
        types.blockStatement([types.expressionStatement(alternate)])
      )
    );
  }
  /** 移除换行符等 */
  removeLine(path, replaceStr = "") {
    let { value } = path.node;
    if (types.isStringLiteral(value)) return;
    let newValue = value.replace(/‮|‫/g, replaceStr);
    if(newValue === value) return;
    path.replaceWith(types.valueToNode(newValue));
  }
}
module.exports = new utils();
